import { Billing } from "@opencode-ai/console-core/billing.js" import { createAsync, query, useParams } from "@solidjs/router" import { createMemo, For, Show, Switch, Match, createEffect, createSignal } from "solid-js" import { formatDateUTC, formatDateForTable } from "../../common" import { withActor } from "~/context/auth.withActor" import { IconChevronLeft, IconChevronRight, IconBreakdown } from "~/component/icon" import styles from "./usage-section.module.css" import { createStore } from "solid-js/store" import { useI18n } from "~/context/i18n" const PAGE_SIZE = 50 async function getUsageInfo(workspaceID: string, page: number) { "use server" return withActor(async () => { return await Billing.usages(page, PAGE_SIZE) }, workspaceID) } const queryUsageInfo = query(getUsageInfo, "usage.list") export function UsageSection() { const params = useParams() const i18n = useI18n() const usage = createAsync(() => queryUsageInfo(params.id!, 0)) const [store, setStore] = createStore({ page: 0, usage: [] as Awaited> }) const [openBreakdownId, setOpenBreakdownId] = createSignal(null) createEffect(() => { setStore({ usage: usage() }) }, [usage]) createEffect(() => { if (!openBreakdownId()) return const handleClickOutside = (e: MouseEvent) => { const target = e.target as HTMLElement if (!target.closest('[data-slot="tokens-with-breakdown"]')) { setOpenBreakdownId(null) } } document.addEventListener("click", handleClickOutside) return () => document.removeEventListener("click", handleClickOutside) }) const hasResults = createMemo(() => store.usage && store.usage.length > 0) const canGoPrev = createMemo(() => store.page > 0) const canGoNext = createMemo(() => store.usage && store.usage.length === PAGE_SIZE) const calculateTotalInputTokens = (u: Awaited>[0]) => { return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWrite5mTokens ?? 0) + (u.cacheWrite1hTokens ?? 0) } const calculateTotalOutputTokens = (u: Awaited>[0]) => { return u.outputTokens + (u.reasoningTokens ?? 0) } const goPrev = async () => { const usage = await getUsageInfo(params.id!, store.page - 1) setStore({ page: store.page - 1, usage, }) } const goNext = async () => { const usage = await getUsageInfo(params.id!, store.page + 1) setStore({ page: store.page + 1, usage, }) } return (

{i18n.t("workspace.usage.title")}

{i18n.t("workspace.usage.subtitle")}

{i18n.t("workspace.usage.empty")}

} > {(usage, index) => { const date = createMemo(() => new Date(usage.timeCreated)) const totalInputTokens = createMemo(() => calculateTotalInputTokens(usage)) const totalOutputTokens = createMemo(() => calculateTotalOutputTokens(usage)) const inputBreakdownId = `input-breakdown-${index()}` const outputBreakdownId = `output-breakdown-${index()}` const isInputOpen = createMemo(() => openBreakdownId() === inputBreakdownId) const isOutputOpen = createMemo(() => openBreakdownId() === outputBreakdownId) const isClaude = usage.model.toLowerCase().includes("claude") return ( ) }}
{i18n.t("workspace.usage.table.date")} {i18n.t("workspace.usage.table.model")} {i18n.t("workspace.usage.table.input")} {i18n.t("workspace.usage.table.output")} {i18n.t("workspace.usage.table.cost")} {i18n.t("workspace.usage.table.session")}
{formatDateForTable(date())} {usage.model}
e.stopPropagation()}> setOpenBreakdownId(null)}>{totalInputTokens()}
e.stopPropagation()}>
{i18n.t("workspace.usage.breakdown.input")} {usage.inputTokens}
{i18n.t("workspace.usage.breakdown.cacheRead")} {usage.cacheReadTokens ?? 0}
{i18n.t("workspace.usage.breakdown.cacheWrite")} {usage.cacheWrite5mTokens ?? 0}
e.stopPropagation()}> setOpenBreakdownId(null)}>{totalOutputTokens()}
e.stopPropagation()}>
{i18n.t("workspace.usage.breakdown.output")} {usage.outputTokens}
{i18n.t("workspace.usage.breakdown.reasoning")} {usage.reasoningTokens ?? 0}
${((usage.cost ?? 0) / 100000000).toFixed(4)}}> {i18n.t("workspace.usage.subscription", { amount: ((usage.cost ?? 0) / 100000000).toFixed(4), })} {i18n.t("workspace.usage.lite", { amount: ((usage.cost ?? 0) / 100000000).toFixed(4), })} {i18n.t("workspace.usage.byok", { amount: ((usage.cost ?? 0) / 100000000).toFixed(4), })} {usage.sessionID?.slice(-8) ?? "-"}
) }